#include "nes_ppu.h"
#include "string.h"
//--------------
#ifndef WIN32
#include "sys_lcd_conf.h"
#include "sys_touch_conf.h"
#include "sys_uart.h"
#include "sys_lcd.h"
#include "sys_defe.h"
#include "sys_cache.h"
#endif
//--------------
#include "nes_main.h"
#include "nes_mapper.h"

extern int emu_disk_led_show; // in desktop.c

uint8* PPU_patterntables;       //8192  VROM开始地址 图案表

//NES显示缓存
__align(1024) unsigned int NES_BUFF[3][NES_DH][NES_DW + 64];
__align(4) unsigned int NES_Palette_argb8888[64];
int buff_inx=0;
int buff_inx_n=0;

void set_tile_banks(uint8 *bank0, uint8 *bank1, uint8 *bank2, uint8 *bank3,
	  uint8 *bank4, uint8 *bank5, uint8 *bank6, uint8 *bank7)
{
	ppu->PPU_tile_banks[0] = bank0;
	ppu->PPU_tile_banks[1] = bank1;
	ppu->PPU_tile_banks[2] = bank2;
	ppu->PPU_tile_banks[3] = bank3;
	ppu->PPU_tile_banks[4] = bank4;
	ppu->PPU_tile_banks[5] = bank5;
	ppu->PPU_tile_banks[6] = bank6;
	ppu->PPU_tile_banks[7] = bank7;
}
//*******************************************************************
  void set_tile_bank(int i, uint8 *bank)
  {
	  ppu->PPU_tile_banks[i] = bank;
  }

  void set_name_table(uint8 bank, int bank_num)
  {
	  ppu->PPU_VRAM_banks[bank] = ppu->PPU_nametables + ((bank_num & 0x03) << 10);
  }

  void set_vram_mem(int bank, void* mem)
  {
	  if (bank < 8)
		ppu->PPU_tile_banks[bank] = mem;

	  ppu->PPU_VRAM_banks[bank] = mem;
  }

  // 0x2000
  uint32 NMI_enabled(void)  { return ppu->LowRegs[0] & 0x80; }
  uint32 sprites_8x16() { return ppu->LowRegs[0] & 0x20; }

  // 0x2001
  uint32 spr_enabled(void)    { return ppu->LowRegs[1] & 0x10; }
  uint32 bg_enabled(void)     { return ppu->LowRegs[1] & 0x08; }
  uint32 spr_clip_left8() { return !(ppu->LowRegs[1] & 0x04); }
  uint32 bg_clip_left8()  { return !(ppu->LowRegs[1] & 0x02); }
  uint32 rgb_pal()        { return ppu->LowRegs[1] & 0xE0;}
//  uint8  rgb_bak;

  // 0x2002
  uint32 sprite0_hit()                     { return ppu->LowRegs[2] & 0x40; }
  uint32 more_than_8_sprites_on_cur_line() { return ppu->LowRegs[2] & 0x20; }
  uint32 VRAM_accessible()                 { return ppu->LowRegs[2] & 0x10; }

  // by rinao
//  uint8* get_patt() { return PPU_patterntables; }
//  uint8* get_namt() { return ppu->PPU_nametables; }
//  uint8 get_pattype(uint8 bank) { return PPU_patterntype[bank]; }
//  void set_pattype(uint8 bank, uint8 data) { PPU_patterntype[bank] = data; }

  //*****************************************************************

//  ROM_banks = (uint8*)malloc(header.num_16k_rom_banks * (16*1024));
//  VROM_banks = (uint8*)malloc(header.num_8k_vrom_banks * (8*1024));
//  VROM_tiles = (uint8 *)malloc(RomHeader->num_8k_vrom_banks * 8 * 1024 / 16 * BYTES_PER_COMPILED_TILE);
//  compile(header.num_8k_vrom_banks * 8 * 1024 / 16, VROM_banks, VROM_tiles);

//  uint8* get_ROM_banks()  { return ROM_banks;   }
//  uint8* get_VROM_banks() { return VROM_banks;  }
//  uint8* get_VROM_tiles() { return VROM_tiles;  }

// #define MASK_BANK(bank,mask) (bank) = ((bank) & (mask))
// #define VALIDATE_VROM_BANK(bank) \
//    MASK_BANK(bank,VROM_mask); \
//    if((bank) >= (RomHeader->num_8k_vrom_banks * 8)) return;

//****************************************************************************************************

#define EXTRACT_4_PIXELS() \
	col = 0; \
	if(pattern_lo & pattern_mask) col |= (0x01 << 6); \
	if(pattern_hi & pattern_mask) col |= (0x02 << 6); \
	pattern_mask >>= 1; \
	if(pattern_lo & pattern_mask) col |= (0x01 << 4); \
	if(pattern_hi & pattern_mask) col |= (0x02 << 4); \
	pattern_mask >>= 1; \
	if(pattern_lo & pattern_mask) col |= (0x01 << 2); \
	if(pattern_hi & pattern_mask) col |= (0x02 << 2); \
	pattern_mask >>= 1; \
	if(pattern_lo & pattern_mask) col |= (0x01 << 0); \
	if(pattern_hi & pattern_mask) col |= (0x02 << 0); \
	*p++= col;

void compile1(int count, uint8 *src, uint8 *dest)
{
	uint8 *p = dest;
	uint8 col;
	uint8 pattern_lo;
	uint8 pattern_hi;
	int i,line,pattern_mask;
	for (i = 0; i < count; i++) {
		for (line = 0; line < 8; line++) {
			pattern_lo = *src;
			pattern_hi = *(src + 8);

			pattern_mask = 0x80;
			EXTRACT_4_PIXELS();
			pattern_mask >>= 1;
			EXTRACT_4_PIXELS();
			src++;
		}
		src += 8;
	}
}
void compile(int count, uint8 *src, uint8 *dest)
{
	u8 destemp[16];
	u8 *p = destemp;
	u8 col;
	u8 pattern_lo;
	u8 pattern_hi;
	int i,line,pattern_mask;
	u8 j;
	for (i = 0; i < count; i++)
	{
		for (line = 0; line < 8; line++)
		{
			pattern_lo = *src;
			pattern_hi = *(src + 8);
			pattern_mask = 0x80;
			EXTRACT_4_PIXELS();
			pattern_mask >>= 1;
			EXTRACT_4_PIXELS();
			src++;
		}
		p=destemp;//重新指向数组首地址
		for(j=0;j<16;j++)*dest++=destemp[j];
		src += 8;
	}
}
#define UPDATE_PIXEL() \
  if(data & pattern_mask) *p |= bit; \
  p++;


#define VRAM(addr) \
  ppu->PPU_VRAM_banks[(addr) >> 10][(addr) & 0x3FF]

#define TILE(addr) \
  (ppu->PPU_tile_banks[(addr) >> 10] + ((addr) & 0x3FF) / 16 * 16)

#define TILE_OFFSET(line) \
  ((line) * 2)

/*
scanline start (if background or sprites are enabled):
	v:0000010000011111=t:0000010000011111
*/
#define LOOPY_SCANLINE_START(v,t) \
  { \
	v = (v & 0xFBE0) | (t & 0x041F); \
  }


#define LOOPY_NEXT_LINE(v) \
  { \
	if((v & 0x7000) == 0x7000) /* is subtile y offset == 7? */ \
	{ \
	  v &= 0x8FFF; /* subtile y offset = 0 */ \
	  if((v & 0x03E0) == 0x03A0) /* name_tab line == 29? */ \
	  { \
		v ^= 0x0800;  /* switch nametables (bit 11) */ \
		v &= 0xFC1F;  /* name_tab line = 0 */ \
	  } \
	  else \
	  { \
		if((v & 0x03E0) == 0x03E0) /* line == 31? */ \
		{ \
		  v &= 0xFC1F;  /* name_tab line = 0 */ \
		} \
		else \
		{ \
		  v += 0x0020; \
		} \
	  } \
	} \
	else \
	{ \
	  v += 0x1000; /* next subtile y offset */ \
	} \
  }


#define LOOPY_NEXT_TILE(v) \
  { \
	if((v & 0x001F) == 0x001F) \
	{ \
	  v ^= 0x0400; /* switch nametables (bit 10) */ \
	  v &= 0xFFE0; /* tile x = 0 */ \
	} \
	else \
	{ \
	  v++; /* next tile */ \
	} \
  }

#define LOOPY_NEXT_PIXEL(v,x) \
  { \
	if(x == 0x07) \
	{ \
	  LOOPY_NEXT_TILE(v); \
	  x = 0x00; \
	} \
	else \
	{ \
	  x++; \
	} \
  }
void PPU_Latch_FDFE(uint32 addr) {}
#define CHECK_MMC2(addr) \
  if(((addr) & 0x0FC0) == 0x0FC0) \
  { \
	if((((addr) & 0x0FF0) == 0x0FD0) || (((addr) & 0x0FF0) == 0x0FE0)) \
	{ \
	  PPU_Latch_FDFE(addr); \
	} \
  }


void PPU_reset(void)
{
  // reset registers
/* memset(LowRegs, 0x00, sizeof(LowRegs));
  HighReg0x4014 = 0x00;

  // clear sprite RAM
  memset(ppu->spr_ram, 0x00, sizeof(ppu->spr_ram));

  // clear palettes
  memset(bg_pal,  0x00, sizeof(bg_pal));
  memset(spr_pal, 0x00, sizeof(spr_pal));

  // clear solid buffer
  memset(ppu->solid_buf, 0x00, sizeof(ppu->solid_buf));

  // clear pattern tables

//  memset(PPU_patterntables, 0x00, sizeof(PPU_patterntables));
  memset(PPU_patterntype, 0x00, sizeof(PPU_patterntype));

  memset(ppu->PPU_tile_tables, 0, sizeof(ppu->PPU_tile_tables));

  // clear internal name tables
  memset(ppu->PPU_nametables, 0x00, sizeof(ppu->PPU_nametables));

  // clear VRAM page table
  memset(ppu->PPU_VRAM_banks, 0x00, sizeof(ppu->PPU_VRAM_banks));

  memset(ppu->PPU_tile_banks, 0, sizeof(ppu->PPU_tile_banks));*/

  // set up PPU memory space table
  PPU_patterntables=VROM_banks;//不确定正确

  ppu->PPU_VRAM_banks[0x00] = PPU_patterntables + (0*0x400);
  ppu->PPU_VRAM_banks[0x01] = PPU_patterntables + (1*0x400);
  ppu->PPU_VRAM_banks[0x02] = PPU_patterntables + (2*0x400);
  ppu->PPU_VRAM_banks[0x03] = PPU_patterntables + (3*0x400);

  ppu->PPU_VRAM_banks[0x04] = PPU_patterntables + (4*0x400);
  ppu->PPU_VRAM_banks[0x05] = PPU_patterntables + (5*0x400);
  ppu->PPU_VRAM_banks[0x06] = PPU_patterntables + (6*0x400);
  ppu->PPU_VRAM_banks[0x07] = PPU_patterntables + (7*0x400);

  // point nametables at internal name table 0
  ppu->PPU_VRAM_banks[0x08] = ppu->PPU_nametables;
  ppu->PPU_VRAM_banks[0x09] = ppu->PPU_nametables;
  ppu->PPU_VRAM_banks[0x0A] = ppu->PPU_nametables;
  ppu->PPU_VRAM_banks[0x0B] = ppu->PPU_nametables;

  ppu->PPU_tile_tables=VROM_tiles;//不确定正确
													 //BYTES_PER_COMPILED_TILE=16
  ppu->PPU_tile_banks[0x00] = ppu->PPU_tile_tables + (0*0x400);
  ppu->PPU_tile_banks[0x01] = ppu->PPU_tile_tables + (1*0x400);
  ppu->PPU_tile_banks[0x02] = ppu->PPU_tile_tables + (2*0x400);
  ppu->PPU_tile_banks[0x03] = ppu->PPU_tile_tables + (3*0x400);
  ppu->PPU_tile_banks[0x04] = ppu->PPU_tile_tables + (4*0x400);
  ppu->PPU_tile_banks[0x05] = ppu->PPU_tile_tables + (5*0x400);
  ppu->PPU_tile_banks[0x06] = ppu->PPU_tile_tables + (6*0x400);
  ppu->PPU_tile_banks[0x07] = ppu->PPU_tile_tables + (7*0x400);

  ppu->read_2007_buffer = 0x00;
  ppu->in_vblank = 0;
  ppu->bg_pattern_table_addr = 0;
  ppu->spr_pattern_table_addr = 0;
  ppu->ppu_addr_inc = 0;
  ppu->loopy_v = 0;
  ppu->loopy_t = 0;
  ppu->loopy_x = 0;
  ppu->toggle_2005_2006 = 0;
  ppu->spr_ram_rw_ptr = 0;
  ppu->read_2007_buffer = 0;
  ppu->current_frame_line = 0;

  if(RomHeader->flags_1 &0x01)set_mirroring(0,1,0,1);//垂直镜像	//  PPU_set_mirroring(); 设置镜像
  else set_mirroring(0,0,1,1);//水平镜像

}
//***********************************************************************************************
void set_mirroring(uint32 nt0, uint32 nt1, uint32 nt2, uint32 nt3)//设置垂直水平镜像
{
  ppu->PPU_VRAM_banks[0x08] = ppu->PPU_nametables + (nt0 << 10); // * 0x0400
  ppu->PPU_VRAM_banks[0x09] = ppu->PPU_nametables + (nt1 << 10);
  ppu->PPU_VRAM_banks[0x0A] = ppu->PPU_nametables + (nt2 << 10);
  ppu->PPU_VRAM_banks[0x0B] = ppu->PPU_nametables + (nt3 << 10);
//  name_table_switched = TRUE;
}

void PPU_start_frame(void)
{
	ppu->current_frame_line = 0;

	if (spr_enabled() || bg_enabled())
		ppu->loopy_v = ppu->loopy_t;
}

uint8 getBGColor() { return ppu->bg_pal[0]; }

void do_scanline_and_draw(uint8* buf)
{
	uint16 i;

	if (!bg_enabled()) {
		// set to background color设置背景颜色
//		memset(buf, bg_pal[0], NES_BACKBUF_WIDTH);//NES_BACKBUF_WIDTH=256+(2*8)
		for (i = 0; i < (256 + 16); i++) {
			buf[i] = ppu->bg_pal[0];
		}
	}

	if (spr_enabled() || bg_enabled()) {
		LOOPY_SCANLINE_START(ppu->loopy_v, ppu->loopy_t);

		if (bg_enabled()) {
			// draw background画背景
			render_bg(buf);
		} else {
			// clear out solid buffer清除固体缓冲区
			memset(ppu->solid_buf, 0x00, sizeof(ppu->solid_buf));
		}

		if (spr_enabled()) {
			// draw sprites绘制精灵
			render_spr(buf);
		}

		LOOPY_NEXT_LINE(ppu->loopy_v);
	}

	ppu->current_frame_line++;
}

void frame_post_draw(unsigned int* fb)
{
	int x, y;
	int disk_access;

	disk_access = emu_disk_led_show ? NES_Mapper->Get(MGI_DISK_ACCESS) : 0;

	// draw disk access LED
	for (y = 8; y < 12; y++) {
		for (x = 8; x < 14; x++) {
			fb[y * (NES_DW + 64) + (x + 32 + 256)] = disk_access ? 0xFF00FF00 : 0;
		}
	}
}

void scanline_draw(int LineNo)
{
	int i;
	int defe_inx;

	do_scanline_and_draw(ppu->dummy_buffer);

	for (i = 0; i < NES_DW; i++)
		NES_BUFF[buff_inx][LineNo][32 + i] = NES_Palette_argb8888[ppu->dummy_buffer[i + 8]];

	if (LineNo >= 239) {//一帧结束-切换下一个缓存
		frame_post_draw(NES_BUFF[buff_inx]);

		//切换下一个nes缓存
		buff_inx++;
		if (buff_inx >= 3) buff_inx = 0;

		//切换defe缓存
		defe_inx = buff_inx + 1;
		if (defe_inx >= 3) defe_inx = 0;

		Defe_conversion_buff((u32)(NES_BUFF[defe_inx]));

		//开启defe
		buff_inx_n++;
		if (buff_inx_n == 3)
			Enable_Layer_Video();
	}
}

//变换颜色缓存RGB565到ARGB8888
void NES_Palette_to_argb8888(void)
{
	int i = 0;
	for (i = 0; i < 64; i++) {
		NES_Palette_argb8888[i] =
			((u64_t)0xFF << 24) |//A
			((((NES_Palette[i] >> 11) & 0x1F) << 3) << 16) | //R
			((((NES_Palette[i] >>  5) & 0x3F) << 2) <<  8) | //G
			((((NES_Palette[i] >>  0) & 0x1F) << 3) <<  0);  //B
	}
}

void do_scanline_and_dont_draw(int LineNo)
{
//	uint16 i;
	// mmc2 / punchout -- we must simulate the ppu for every line
//	if(parent_NES->ROM->get_mapper_num() == 9)
//	{
//		do_scanline_and_draw(ppu->dummy_buffer);
//	}
//	else
	{
		// if sprite 0 flag not set and sprite 0 on current line
		if((!sprite0_hit()) &&
			(ppu->current_frame_line >= ((uint32)(spr_ram[0]+1))) &&
			(ppu->current_frame_line <  ((uint32)(spr_ram[0]+1+(sprites_8x16()?16:8))))
			)
		{
			// render line to dummy buffer
			do_scanline_and_draw(ppu->dummy_buffer);

		}
		else
		{
			if(spr_enabled() || bg_enabled())
			{
				LOOPY_SCANLINE_START(ppu->loopy_v, ppu->loopy_t);
				LOOPY_NEXT_LINE(ppu->loopy_v);
			}
			ppu->current_frame_line++;
		}
	}

}
void start_vblank()
{
  ppu->in_vblank = 1;

  // set vblank register flag
  ppu->LowRegs[2] |= 0x80;
}

void end_vblank()
{
  ppu->in_vblank = 0;

  // reset vblank register flag and sprite0 hit flag1
  ppu->LowRegs[2] &= 0x3F;
}


// these functions read from/write to VRAM using loopy_v
uint8 read_2007()
{
  uint16 addr;
  uint8 temp;

  addr = ppu->loopy_v;
  ppu->loopy_v += ppu->ppu_addr_inc;

  addr &= 0x3FFF;

  if(addr >= 0x3000)
  {
	// is it a palette entry?
	if(addr >= 0x3F00)
	{
	  // palette

	  // handle palette mirroring
	  if(0x0000 == (addr & 0x0010))
	  {
		// background palette
		return ppu->bg_pal[addr & 0x000F];
	  }
	  else
	  {
		// sprite palette
		return ppu->spr_pal[addr & 0x000F];
	  }
	}

	// handle mirroring
	addr &= 0xEFFF;
  }

  temp = ppu->read_2007_buffer;
  ppu->read_2007_buffer = VRAM(addr);

  return temp;
}

void write_2007(uint8 data)
{
  uint16 addr;

  addr = ppu->loopy_v;
  ppu->loopy_v += ppu->ppu_addr_inc;

  addr &= 0x3FFF;

  if(addr >= 0x3000)
  {
	// is it a palette entry?
	if(addr >= 0x3F00)
	{
	  // palette
	  data &= 0x3F;

	  if(0x0000 == (addr & 0x000F)) // is it THE 0 entry?0的条目吗
	  {
		ppu->bg_pal[0] = ppu->spr_pal[0] = data;
	  }
	  else if(0x0000 == (addr & 0x0010))
	  {
		// background palette
		ppu->bg_pal[addr & 0x000F] = data;
	  }
	  else
	  {
		// sprite palette
		ppu->spr_pal[addr & 0x000F] = data;
	  }

	  return;
	}

	// handle mirroring处理镜像
	addr &= 0xEFFF;
  }

  if (addr >= 0x23D0 && addr < 0x23F0)
	  addr = addr;

  VRAM(addr) = data;
  if (addr < 0x2000) {
#if	NES_RAM_SPEED==1  //1:内存占用小 0:速度快

	  *(TILE(addr) + (addr & 0xf )) = data;//************************
#else
	  update_tile(addr, data);
#endif
  }
}

#define UPDATE_4_PIXELS() \
	col = 0; \
	if(data & pattern_mask) col |= (bit << 6); \
	pattern_mask >>= 1; \
	if(data & pattern_mask) col |= (bit << 4); \
	pattern_mask >>= 1; \
	if(data & pattern_mask) col |= (bit << 2); \
	pattern_mask >>= 1; \
	if(data & pattern_mask) col |= (bit << 0); \
	*p++ |= col;

void update_tile(int byte_offset, uint8 data)
{
	//int tileNum = byte_offset >> 4;
	int line = byte_offset & 0xf;

	uint8 *p = TILE(byte_offset) + TILE_OFFSET(line & 0x7);
	uint8 bit;
	uint8 pattern_mask;
	int col;

	if (line < 8) {
		// low pattern
		bit = 0x01;

		*(uint16 *)p &= 0xaaaa;

	} else {
		// high pattern
		bit = 0x02;
		*(uint16 *)p &= 0x5555;
	}

	pattern_mask = 0x80;


	UPDATE_4_PIXELS();
	pattern_mask >>= 1;
	UPDATE_4_PIXELS();

}

uint8 PPU_ReadFromPort(uint16 addr)
{
  uint8 temp;


  //switch(addr)
  switch(addr & 0x7)
  {
	//case 0x2002:
	case 0x2:
		// clear toggle
		ppu->toggle_2005_2006 = 0;

		temp = ppu->LowRegs[2];

		// clear v-blank flag
		ppu->LowRegs[2] &= 0x7F;

		return temp;

//      break;

	//case 0x2007:
	case 0x7:
	  return read_2007();
//      break;

  }

  return ppu->LowRegs[addr & 0x0007];
}

extern u8 cpunmi; // in nes_cpu.c

void  PPU_WriteToPort(uint8 data,uint16 addr )
{

//	uint32 t;

  //switch(addr)
  switch(addr & 0x7)
  {
	//case 0x2000:
	case 0:
	{
	  ppu->bg_pattern_table_addr  = (data & 0x10) ? 0x1000 : 0x0000;
	  ppu->spr_pattern_table_addr = (data & 0x08) ? 0x1000 : 0x0000;
	  ppu->ppu_addr_inc = (data & 0x04) ? 32 : 1;

	  // t:0000110000000000=d:00000011
	  ppu->loopy_t = (ppu->loopy_t & 0xF3FF) | (((uint16)(data & 0x03)) << 10);

	  if ((data & 0x80) && !(ppu->LowRegs[0] & 0x80) && (ppu->LowRegs[2] & 0x80)) {
			cpunmi = 1;	// hmm..解决古诗语音花屏
	  }

	  break;
	}

	// Rick, lazy updating stuff
	//case 0x2001:
//    case 1:
//		bg_enabled();
//
//		spr_enabled();
//	  break;

	//case 0x2003:
	case 3:
	  ppu->spr_ram_rw_ptr = data;
	  break;

	//case 0x2004:
	case 4:
	  spr_ram[ppu->spr_ram_rw_ptr++] = data;
	  break;

	//case 0x2005:
	case 5:
	  ppu->toggle_2005_2006 = !ppu->toggle_2005_2006;

	  if(ppu->toggle_2005_2006)
	  {
		// first write

		// t:0000000000011111=d:11111000
		ppu->loopy_t = (ppu->loopy_t & 0xFFE0) | (((uint16)(data & 0xF8)) >> 3);

		// x=d:00000111
		ppu->loopy_x = data & 0x07;

	  }
	  else
	  {
		// second write

		// t:0000001111100000=d:11111000
		ppu->loopy_t = (ppu->loopy_t & 0xFC1F) | (((uint16)(data & 0xF8)) << 2);

		// t:0111000000000000=d:00000111
		ppu->loopy_t = (ppu->loopy_t & 0x8FFF) | (((uint16)(data & 0x07)) << 12);

	  }

	  break;

	//case 0x2006:
	case 6:
	  ppu->toggle_2005_2006 = !ppu->toggle_2005_2006;

	  if(ppu->toggle_2005_2006)
	  {
		// first write

		  // t:0011111100000000=d:00111111
		  // t:1100000000000000=0
		ppu->loopy_t = (ppu->loopy_t & 0x00FF) | (((uint16)(data & 0x3F)) << 8);
	  }
	  else
	  {
		// second write
		// t:0000000011111111=d:11111111
		ppu->loopy_t = (ppu->loopy_t & 0xFF00) | ((uint16)data);

		  // v=t
		ppu->loopy_v = ppu->loopy_t;
	  }

	  break;

	//case 0x2007:
	case 7:
	  write_2007(data);
	  break;
  }

  ppu->LowRegs[addr & 0x0007] = data;
}

//*****************************************************************************
#define DRAW_BG_PIXEL() \
  col = attrib_bits; \
 \
  if(pattern_lo & pattern_mask) col |= 0x01; \
  if(pattern_hi & pattern_mask) col |= 0x02; \
 \
  if(col & 0x03) \
  { \
	*p = ppu->bg_pal[col]; \
	/* set solid flag */ \
	*solid = BG_WRITTEN_FLAG; \
  } \
  else \
  { \
	*p =  ppu->bg_pal[0]; \
	/* set solid flag */ \
	*solid = 0; \
  } \
  solid++; \
  p++; \
//***************************************************************************************
#define NEW_BG_PIXEL() \
	if (col) {\
		col |= attrib_bits; \
		*solid = BG_WRITTEN_FLAG; \
	} else { \
		col = 0; \
		*solid = 0; \
	} \
	*p = ppu->bg_pal[col]; \
	solid++; \
	p++; \


void render_bg(uint8* buf)
{
  uint8 *p;
  uint32 i;
  uint8 *solid;
  uint8 *data;
  uint32 col;

  uint32 tile_x; // pixel coords within nametable像素坐标内nametable
  uint32 tile_y;
  uint32 name_addr;
  uint32 line;
  uint32 pattern_addr;

  uint32 attrib_addr;
  uint32 attrib_bits;

#if	NES_RAM_SPEED==1  //1:内存占用小 0:速度快
		uint8 pattern_lo;
		uint8 pattern_hi;
		uint8 pattern_mask;
#else
		int col2;
#endif

  tile_x = (ppu->loopy_v & 0x001F);
  tile_y = (ppu->loopy_v & 0x03E0) >> 5;

  name_addr = 0x2000 + (ppu->loopy_v & 0x0FFF);

  attrib_addr = 0x2000 + (ppu->loopy_v & 0x0C00) + 0x03C0 + ((tile_y & 0xFFFC)<<1) + (tile_x>>2);
  if(0x0000 == (tile_y & 0x0002))
	if(0x0000 == (tile_x & 0x0002))
	  attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
	else
	  attrib_bits = (VRAM(attrib_addr) & 0x0C);
  else
	if(0x0000 == (tile_x & 0x0002))
	  attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
	else
	  attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;

  p     = buf       + (SIDE_MARGIN - ppu->loopy_x);
  solid = ppu->solid_buf + (SIDE_MARGIN - ppu->loopy_x); // set "solid" buffer ptr


  line = (ppu->loopy_v & 0x7000) >> 12;

  // draw 33 tiles
  for(i = 33; i; i--)
  {

	pattern_addr = ppu->bg_pattern_table_addr + ((int32)VRAM(name_addr) << 4) + line;

 //   CHECK_MMC2(pattern_addr);//*******************************************************

#if	NES_RAM_SPEED==1  //1:内存占用小 0:速度快
		data = TILE(pattern_addr) + line;

		pattern_lo = *data;
		pattern_hi = *(data + 8);
		pattern_mask = 0x80;

		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
		pattern_mask >>= 1;
		DRAW_BG_PIXEL();
#else
		data = TILE(pattern_addr) + TILE_OFFSET(line);

		col2 = *data++;
		col = col2 >> 6;
		NEW_BG_PIXEL();
		col = (col2 >> 4) & 0x03;
		NEW_BG_PIXEL();
		col = (col2 >> 2) & 0x03;
		NEW_BG_PIXEL();
		col = col2 & 0x03;
		NEW_BG_PIXEL();

		col2 = *data++;
		col = col2 >> 6;
		NEW_BG_PIXEL();
		col = (col2 >> 4) & 0x03;
		NEW_BG_PIXEL();
		col = (col2 >> 2) & 0x03;
		NEW_BG_PIXEL();
		col = col2 & 0x03;
		NEW_BG_PIXEL();
#endif

	tile_x++;
	name_addr++;

	// are we crossing a dual-tile boundary?我们穿越dual-tile边界吗?
	if(0x0000 == (tile_x & 0x0001))
	{
	  // are we crossing a quad-tile boundary?
	  if(0x0000 == (tile_x & 0x0003))
	  {
		// are we crossing a name table boundary?
		if(0x0000 == (tile_x & 0x001F))
		{
		  name_addr ^= 0x0400; // switch name tables
		  attrib_addr ^= 0x0400;
		  name_addr -= 0x0020;
		  attrib_addr -= 0x0008;
		  tile_x -= 0x0020;
		}

		attrib_addr++;
	  }

	  if(0x0000 == (tile_y & 0x0002))
		if(0x0000 == (tile_x & 0x0002))
		  attrib_bits = (VRAM(attrib_addr) & 0x03) << 2;
		else
		  attrib_bits = (VRAM(attrib_addr) & 0x0C);
	  else
		if(0x0000 == (tile_x & 0x0002))
		  attrib_bits = (VRAM(attrib_addr) & 0x30) >> 2;
		else
		  attrib_bits = (VRAM(attrib_addr) & 0xC0) >> 4;
	}
  }

//  if(bg_clip_left8())
//  {
//    // clip left 8 pixels夹了8个像素SIDE_MARGIN = 8,
//	memset(buf + SIDE_MARGIN,  bg_pal[0], 8);
//    memset(solid + SIDE_MARGIN, 0, sizeof(solid[0])*8);
//  }
}
void render_spr(uint8* buf)
{
	int32 s;              // sprite #
	int32  spr_x;         // sprite coordinates
	uint32 spr_y;
	uint8* spr;           // pointer to sprite RAM entry
	uint8* p;             // draw pointer
	int line;
	//uint32 *solid;
	uint8 *solid;
	uint32 priority;
	uint32 tile_addr;
	int32 inc_x;           // drawing vars
	int32 start_x, end_x;
	int32 x,y;             // in-sprite coords
	uint32 col;
	uint32 num_sprites = 0;
	uint8 *t;

	uint32 spr_height;

#if	NES_RAM_SPEED==1  //1:内存占用小 0:速度快

		uint8 pattern_lo;
		uint8 pattern_hi;
#else
		uint32 pattern;
#endif

	spr_height = sprites_8x16() ? 16 : 8;

	//for(s = 0; s < 64; s++)
	for(s = 0, spr = spr_ram; s < 64; s++, spr+=4)
	{

		spr_y = spr[0]+1;

		// on current scanline对当前扫描线?
		if((spr_y > ppu->current_frame_line) || ((spr_y+(spr_height)) <= ppu->current_frame_line))
			continue;

		num_sprites++;
		if(num_sprites > 8)
		{
			/*if(!show_more_than_8_sprites)*/ break;//**************************************
		}

		// get x coord
		spr_x = spr[3];

		start_x = 0;
		end_x = 8;

		// clip right
		if((spr_x + 7) > 255)
		{
			end_x -= ((spr_x + 7) - 255);
		}

		// clip left
		if((spr_x < 8) && (spr_clip_left8()))
		{
			if(0 == spr_x) continue;
			start_x += (8 - spr_x);
		}

		y = ppu->current_frame_line - spr_y;

//		CHECK_MMC2(spr[1] << 4);//**************************************************************************

		// calc offsets into buffers
		p = &buf[SIDE_MARGIN + spr_x + start_x];
		solid = &ppu->solid_buf[SIDE_MARGIN + spr_x + start_x];

		// flip horizontally?
		if(spr[2] & 0x40) // yes
		{
			inc_x = -1;
			start_x = (8-1) - start_x;
			end_x = (8-1) - end_x;
		}
		else
		{
			inc_x = 1;
		}

		// flip vertically?
		if(spr[2] & 0x80) // yes
		{
			y = (spr_height-1) - y;
		}
		line = y & 7;

		// get priority bit
		priority = spr[2] & 0x20;

		tile_addr = spr[1] << 4;
		if(sprites_8x16()) {
			if(spr[1] & 0x01) {
				tile_addr += 0x1000;
				if(y < 8) tile_addr -= 16;
			} else {
				if(y >= 8) tile_addr += 16;
			}
		} else {
			tile_addr += ppu->spr_pattern_table_addr;
		}

#if	NES_RAM_SPEED==1  //1:内存占用小 0:速度快
			t = TILE(tile_addr) + line;

			pattern_lo = *t;
			pattern_hi = *(t + 8);

#else
		// read 16bits = 2bits x 8pixels
		t = TILE(tile_addr) + TILE_OFFSET(line);
		pattern = ((uint32)*t << 8) | *(t + 1);

#endif

		for(x = start_x; x != end_x; x += inc_x)
		{
			//uint8 col = 0x00;

			// if a sprite has drawn on this pixel, don't draw anything如果一个雪碧吸引了这个像素,不画任何东西
			if(!((*solid) & SPR_WRITTEN_FLAG))
			{

#if	NES_RAM_SPEED==1  //1:内存占用小 0:速度快

				col = ((pattern_hi>>(7-x)<<1)&2)|((pattern_lo>>(7-x))&1);
#else
				col = pattern >> ((7 - (x & 7)) * 2);
#endif
				col &= 0x03;

				if (col) {
					col |= (spr[2] & 0x03) << 2;

					// set sprite 0 hit flag
					if(!s)
					{
						if((*solid) & BG_WRITTEN_FLAG)
						{
							ppu->LowRegs[2] |= 0x40;
						}
					}

					(*solid) |= SPR_WRITTEN_FLAG;
					if(priority)
					{
						//(*solid) |= SPR_WRITTEN_FLAG;
						if(!((*solid) & BG_WRITTEN_FLAG))
						{
							*p =  ppu->spr_pal[col];
						}
					}
					else
					{

						*p = ppu->spr_pal[col];
					}
				}
			}

			p++;
			solid++;
		}
	}

	if(num_sprites >= 8)
	{
		ppu->LowRegs[2] |= 0x20;
	}
	else
	{
		ppu->LowRegs[2] &= 0xDF;
	}
}








